import React, { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useState } from "react";
import type { TypeValidators } from "./validators";
import styles from "./style.module.scss";
import cn from "classnames";
import { useValidation } from "./Context";
import { Validator } from "./Validator";
import { useValidatedForm } from "./ValidatedForm";

const SuccessIcon = () => {
    return (
        <svg viewBox="64 64 896 896" focusable="false" data-icon="check-circle" width="1em" height="1.05em" fill="currentColor" aria-hidden="true"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm193.5 301.7l-210.6 292a31.8 31.8 0 01-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.2 0 19.9 4.9 25.9 13.3l71.2 98.8 157.2-218c6-8.3 15.6-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.5 12.7z"></path></svg>
    );
}

const FaildIcon = () => {
    return <svg fillRule="evenodd" viewBox="64 64 896 896" focusable="false" data-icon="close-circle" width="1em" height="1.05em" fill="currentColor" aria-hidden="true"><path d="M512 64c247.4 0 448 200.6 448 448S759.4 960 512 960 64 759.4 64 512 264.6 64 512 64zm127.98 274.82h-.04l-.08.06L512 466.75 384.14 338.88c-.04-.05-.06-.06-.08-.06a.12.12 0 00-.07 0c-.03 0-.05.01-.09.05l-45.02 45.02a.2.2 0 00-.05.09.12.12 0 000 .07v.02a.27.27 0 00.06.06L466.75 512 338.88 639.86c-.05.04-.06.06-.06.08a.12.12 0 000 .07c0 .03.01.05.05.09l45.02 45.02a.2.2 0 00.09.05.12.12 0 00.07 0c.02 0 .04-.01.08-.05L512 557.25l127.86 127.87c.04.04.06.05.08.05a.12.12 0 00.07 0c.03 0 .05-.01.09-.05l45.02-45.02a.2.2 0 00.05-.09.12.12 0 000-.07v-.02a.27.27 0 00-.05-.06L557.25 512l127.87-127.86c.04-.04.05-.06.05-.08a.12.12 0 000-.07c0-.03-.01-.05-.05-.09l-45.02-45.02a.2.2 0 00-.09-.05.12.12 0 00-.07 0z"></path></svg>;
};

const ValidatedContext = React.createContext({});

const ValidatedComponent = <TValueType={}>(props: Validation.IValidatedProps<TypeValidators, TValueType>) => {
    const validationApi = useValidation();
    const validateForm = useValidatedForm<any>();
    const hideSuccessMessage = useMemo(() => {
        if(null === props.hideSuccessMessage || undefined === props.hideSuccessMessage) {
            return validateForm.hideSuccessMessage;
        } else {
            return props.hideSuccessMessage;
        }
    }, [props.hideSuccessMessage, validateForm.hideSuccessMessage]);
    const disabledComponent = useMemo(() => {
        if(null === props.disabled || undefined === props.disabled) {
            return validateForm.disabled;
        } else {
            return props.disabled;
        }
    }, [props.disabled, validateForm.disabled]);
    const [ tmpState ] = useState({
        isMount: false
    });
    const [ validateResult, setValidateResult ] = useState({
        positive: false,
        overrideMessage: false,
        message: ""
    });
    const [ inputValue, setInputValue ] = useState<any>(props.value);
    const [ tempState ] = useState({
        value: props.defaultValue,
        takeOverMessage: false
    });
    const onComponentChange = useCallback((eventData: any) => {
        if(validateForm.validateOnBlur || validateForm.validateOnSubmit) {
            tempState.value = eventData;
        } else {
            setInputValue(eventData);
        }
        validateForm.mount && validateForm.setValue(props.id, eventData);
    }, [validateForm]);
    const onInputChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
        let currentValue = null;
        if(["checkbox", "radio"].includes(event.target.type)) {
            currentValue = event.target.checked;
        } else {
            currentValue = event.target.value;
        }
        if(validateForm.validateOnBlur || validateForm.validateOnSubmit) {
            tempState.value = currentValue;
        } else {
            setInputValue(currentValue);
        }
        validateForm.mount && validateForm.setValue(props.id, currentValue);
    }, [validateForm]);
    const onComponentBlur = useCallback(() => {
        if(validateForm.validateOnBlur) {
            setInputValue(tempState.value);
        }
    }, [validateForm]);
    const ChildrenWrapper = useMemo(() => {
        if(props.valueFromChild) {
            const childrenList = React.Children.toArray(props.children);
            return childrenList.map((item) => {
                if(React.isValidElement(item)) {
                    if(typeof (item as any).type === "function") {
                        return React.cloneElement(item as any, {
                            defaultValue: props.defaultValue,
                            onChange: onComponentChange,
                            onBlur: onComponentBlur,
                            disabled: disabledComponent
                        });
                    } else if(typeof (item as any).type === "string" && (item as any).type === "input") {
                        return React.cloneElement(item as any, {
                            defaultValue: props.defaultValue,
                            onChange: onInputChange,
                            onBlur: onComponentBlur,
                            disabled: disabledComponent
                        });
                    } else {
                        return item;
                    }
                } else {
                    return item;
                }
            });
        } else {
            return props.children;
        }
    }, [props.children, onComponentChange, disabledComponent]);
    const ComMessage = validateResult.message;
    const validatedContext = useMemo(() => ({
        takeOverMessage: () => {
            tempState.takeOverMessage = true;
        },
        releaseTakeOverHandle: () => {
            tempState.takeOverMessage = false;
        },
        validateResult,
        hideSuccessMessage: hideSuccessMessage,
        successMessage: props.successMessage,
        OverrideMessage: ComMessage
    }), [tempState, validateResult, hideSuccessMessage, props.successMessage, ComMessage]);
    useEffect(() => {
        if(tmpState.isMount) {
            const rules = props.rule ? [props.rule] : props.rules || [];
            validationApi.updateValidatedRules(props.id, rules, props.tag, props.group);
        }
    },[props.rule, props.rules]);
    useEffect(() => {
        if(tmpState.isMount && (!validateForm.validateOnSubmit || validateForm.validateOnBlur)) {
            let validateResultObj: Validator<any>|undefined;
            if(props.group) {
                validateResultObj = validationApi.validateWithGroupAndId(props.id,props.group, {
                    value: inputValue
                });
            } else {
                validateResultObj = validationApi.validateById(props.id, {
                    value: inputValue,
                    tag: props.tag,
                    group: props.group
                });
            }
            if(validateResultObj) {
                setValidateResult({
                    positive: false,
                    message: validateResultObj.message as any,
                    overrideMessage: typeof validateResultObj.message === "function"
                });
            } else {
                setValidateResult({
                    positive: true,
                    message: "",
                    overrideMessage: false
                });
            }
        }
    }, [inputValue]);
    useEffect(() => {
        if(!tmpState.isMount) {
            const rules = props.rule ? [props.rule] : [];
            validationApi.registe({
                id: props.id,
                group: props.group,
                tag: props.tag,
                rules: [...rules, ...(props.rules || [])]
            });
            tmpState.isMount = true;
            return () => {
                validationApi.unRegiste({
                    id: props.id,
                    group: props.group
                });
            }
        }
    }, []);
    useEffect(() => {
        const rmEvent = validateForm.onValidated({
            id: props.id,
            tag: props.tag,
            group: props.group
        }, (errorValidator) => {
            if( errorValidator) {
                (validateResult.message !== errorValidator.message || validateResult.positive) && setValidateResult({
                    positive: false,
                    message: errorValidator.message as any,
                    overrideMessage: typeof errorValidator.message === "function"
                })
            } else {
                (validateResult.message !== validateResult.message || !validateResult.positive) && setValidateResult({
                    positive: true,
                    message: "",
                    overrideMessage: false
                });
            }
        });
        return () => rmEvent();
    },[validateResult]);

    return (<ValidatedContext.Provider value={validatedContext}>
        {ChildrenWrapper}
        { tmpState.isMount && !tempState.takeOverMessage && !validateResult.positive ? (
            validateResult.overrideMessage ? <ComMessage /> : (<label className={cn(styles.valiated_message, styles.validated_failed, "Validation_Message")}>
                <i><FaildIcon /></i>
                <span>{validateResult.message}</span>
            </label>)
        ) : <></>}
        { tmpState.isMount && !tempState.takeOverMessage && !hideSuccessMessage && validateResult.positive && <label className={cn(styles.valiated_message, styles.validated_success, "Validation_Message")}>
            <i><SuccessIcon /></i>
            <span>{props.successMessage || "验证通过"}</span>
        </label> }
    </ValidatedContext.Provider>);
};

export const ValidatedMessage = () => {
    const validatedContext = useContext(ValidatedContext);
    const { takeOverMessage, releaseTakeOverHandle, validateResult, hideSuccessMessage, successMessage, OverrideMessage } = (validatedContext || {}) as any;
    const [ tempState ] = useState({
        isMount: false
    });
    useLayoutEffect(() => {
        if(takeOverMessage) {
            takeOverMessage();
        }
        return () => {
            releaseTakeOverHandle();
        };
    }, []);
    useEffect(() => {
        if(!tempState.isMount) {
            tempState.isMount = true;
        }
    }, []);
    return (<>
         { tempState.isMount && takeOverMessage && !validateResult.positive ? (
            validateResult.overrideMessage ? <OverrideMessage positive={false} message={validateResult.message} /> : (<label className={cn(styles.valiated_message, styles.validated_failed, "Validation_Message")}>
                <i><FaildIcon /></i>
                <span>{validateResult.message}</span>
            </label>)
        ) : <></>}
        { tempState.isMount && takeOverMessage && !hideSuccessMessage && validateResult.positive && <label className={cn(styles.valiated_message, styles.validated_success, "Validation_Message")}>
            <i><SuccessIcon /></i>
            <span>{successMessage || "验证通过"}</span>
        </label> }
    </>);
}

export const Validated = (<Value extends any>() => ValidatedComponent<Value>)();
